package utils

import (
	"bytes"
	"context"
	"encoding/xml"
	"errors"
	"fmt"
	"gin-luban-server/global"
	"gin-luban-server/model"
	"github.com/bndr/gojenkins"
	"net/http"
	"text/template"
)


// 定义使用到的结构体

// 部署脚本所需内容内容结构体
type deployScriptStruct struct {
	AppInfo model.DeployApp   // 应用的信息
	AppConfig model.DeployAppConfigure  // 应用(服务)配置的信息
	CmdbVirtualList []model.CmdbServer    // 虚拟机信息
}


// 定义整个JOB需要的信息的一个 类
type jobStruct struct {
	Para map[string]string
	ScriptContent string
}
//

// 字符串转出XML格式 - 格式化
// 参考: https://stackoverflow.com/questions/60963797/how-to-convert-a-string-to-xml
func StringToXml(in string) string {
	var b bytes.Buffer
	xml.EscapeText(&b, []byte(in))
	return b.String()
}

// 解析job模板
func getDeployScript(appConfig model.DeployAppConfigure) (content string, err error) {
	// 1. 拼凑发布需要的信息
	var appInfo model.DeployApp
	global.GVA_DB.Where("apps_name = ?",  appConfig.AppsName).First(&appInfo)

	var virtualList []model.DeployAppVirtual
	global.GVA_DB.Where("apps_job_name = ?", appConfig.AppsJobName).Find(&virtualList)

	var virtualCodes []string
	for _, v := range virtualList {
		virtualCodes = append(virtualCodes,  v.VirtualCode)
	}

	//println("virtualCodes:", virtualCodes[0])
	var cmdbVirtualList  []model.CmdbServer
	global.GVA_DB.Where("virtual_code IN ?", virtualCodes).Find(&cmdbVirtualList)


	//fmt.Println(cmdbVirtualList[0].Ipaddress)


	data := deployScriptStruct{
		AppInfo: appInfo,
		AppConfig: appConfig,
		CmdbVirtualList: cmdbVirtualList,
	}

	// 2. 确定应用发布语言使用的模板
	var languageTemplatePath string
	if appConfig.AppsType == "service" {		// java 后端
		languageTemplatePath = "resource/template/deploy/java_spring.conf.tpl"
		//languageTemplatePath = "resource/template/deploy/test.tpl"
	} else if appConfig.AppsType == "web" {
		languageTemplatePath = "resource/template/deploy/vue_portal.conf.tpl"
	} else if appConfig.AppsType == "common" {  // 公共包
		languageTemplatePath = "resource/template/deploy/java_common.conf.tpl"
	} else if appConfig.AppsType == "apk" {   	// 	安卓包
		languageTemplatePath = "resource/template/deploy/android_apk.conf.tpl"
	} else {
		return "", errors.New("deploy languageType not know")
	}

	// 2. 会检测模板语法是否正确
	tmpl, err := template.ParseFiles(languageTemplatePath)
	if err != nil {
		return "", err
	}
	buf := new(bytes.Buffer)
	// 3. 生产模板内容
	if err = tmpl.Execute(buf, data); err != nil {
		return "", err
	}
	deployContent := buf.String()

	fmt.Println("jenkins job script string:", deployContent)
	return StringToXml(deployContent), nil


}

// 解析job模板为XML
func GetJenkinsXml(appConfig model.DeployAppConfigure) (content string, err error) {
	// 1. 获取发布脚本的内容
	deployScript, err := getDeployScript(appConfig)

	if err != nil {
		return "", err
	}

	// 2. 通过jenkins_job模板获取真个JOB 的内容
	// 模板
	jenkinsJobTemplatePath := "resource/template/jenkins/jenkins_job.tpl"
	// 解析
	tmpl, err := template.ParseFiles(jenkinsJobTemplatePath)
	if err != nil {
		return "", err
	}


	jobContent := jobStruct{
		Para: map[string]string{    // 参数内容
			"BRANCH": appConfig.BranchName,
		},
		ScriptContent: deployScript,   // 脚本内容
	}

	// 生产xml job内容
	buf := new(bytes.Buffer)
	if err = tmpl.Execute(buf, jobContent); err != nil {
		fmt.Println(err)
	}

	jenkinsJobContent := buf.String()

	//fmt.Println("jenkins job xml:", jenkinsJobContent)

	return jenkinsJobContent, nil
}






// 创建jenkins函数
func CreateJenkinsJob(jenkinsClient *gojenkins.Jenkins, viewName, jobName, content string, ctx context.Context) (err error) {
	view, _ := jenkinsClient.GetView(ctx,viewName)
	if view.GetName() != "" {
		_, err := jenkinsClient.CreateJob(ctx, content, jobName)
		if err != nil {
			global.GVA_LOG.Error("创建JOB失败失败" + err.Error())
			return errors.New("创建JOB失败失败" + err.Error())
		}
		_,err =view.AddJob(ctx,jobName)
		if err != nil {
			global.GVA_LOG.Error("创建JOB失败失败" + err.Error())
			return errors.New("创建JOB失败失败" + err.Error())
		}
	} else {
		viewNew,_:=jenkinsClient.CreateView(ctx,viewName, gojenkins.LIST_VIEW)

		_, err = jenkinsClient.CreateJob(ctx, content, jobName)
		if err != nil {
			global.GVA_LOG.Error("创建JOB失败失败" + err.Error())
			return errors.New("创建JOB失败失败" + err.Error())
		}
		_,err =viewNew.AddJob(ctx,jobName)
		if err != nil {
			global.GVA_LOG.Error("创建JOB失败失败" + err.Error())
			return errors.New("创建JOB失败失败" + err.Error())
		}
	}
	return
}


//func UpdateJenkinsJob(jenkinsClient *gojenkins.Jenkins, jobName, content string, ctx context.Context) (err error) {
func UpdateJenkinsJob(jenkinsConfig model.SysBasicConfigure, appConfig model.DeployAppConfigure, content string) (err error) {
	client := &http.Client{}
	req, err := http.NewRequest("POST", jenkinsConfig.BaseUrl +  "/job/" + appConfig.AppsJobName +"/config.xml", bytes.NewBuffer([]byte(content)))
	req.SetBasicAuth(jenkinsConfig.BasicUser, jenkinsConfig.BasicPasswd)
	resp, err := client.Do(req)
	// 防止更新配置中 所在机房, 在新的jenkins中没有之旧机房中jenkins上的job
	if err != nil || resp.StatusCode != 200{
		if err == nil {
			fmt.Println("更新jenkins的时候发生了错误","请求的状态码是", resp.StatusCode)
			return errors.New(fmt.Sprintf("更新jenkins的时候发生了错误, 更新请求的状态码是: %d", resp.StatusCode) )
		}
		return err
	}
	if resp.StatusCode == 200 {
		return nil
	}



	return
}
